package com.globo.opensocial;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.util.regex.Pattern;

import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.EvaluatorException;

import com.yahoo.platform.yui.compressor.JavaScriptCompressor;

/**
 * Handle a Javascript code.
 * @author Jorge Falcao
 *
 */
public class JavaScript extends Content {

	boolean debug = false;
	String buffer = null;
	
	static String startTag = "<script type=\"text/javascript\">\n";
	static String endTag = "</script>\n";
	
	static private Pattern paramRegex = Pattern.compile("@([a-z]+)");

		
	JavaScript(Application app, InputStream inputStream) throws FileNotFoundException {
		super(app, inputStream);
		app.info("Reading JS from memory stream");
		buffer = getContent();
	}
	
	JavaScript(Application app, File file) throws FileNotFoundException {
		super(app, file);
		app.info("Reading JS file " + file.getName());
		// We need preload the Core Javascript code to get the annotations.
		buffer = getContent();
		
	}

	public static void writeOpenScript(Writer out) throws IOException {
		out.append(startTag);
	}

	public static void writeCloseScript(Writer out) throws IOException {
		out.append(endTag);
	}

	public static void writeJs(String in, Writer out) throws IOException {
		
		in = replacePlaceHolders(in);
		
		if ( Application.getInstance().isDebug()) {
			out.append(in);
		} else {
			compressJs(new StringReader(in), out);
		}
	}

	private static void compressJs(Reader in, Writer out) throws IOException {
		try {
			JavaScriptCompressor compressor = new JavaScriptCompressor(in,
					new ErrorReporter() {

						public void warning(String message, String sourceName,
								int line, String lineSource, int lineOffset) {
							if (line < 0) {
								System.err.println("\n[WARNING] " + message);
							} else {
								System.err.println("\n" + line + ':'
										+ lineOffset + ':' + message);
							}
						}

						public void error(String message, String sourceName,
								int line, String lineSource, int lineOffset) {
							if (line < 0) {
								System.err.println("\n[ERROR] " + message);
							} else {
								System.err.println("\n" + line + ':'
										+ lineOffset + ':' + message);
							}
						}

						public EvaluatorException runtimeError(String message,
								String sourceName, int line, String lineSource,
								int lineOffset) {
							System.err.println("\n[ERROR] " + message);
							error(message, sourceName, line, lineSource,
									lineOffset);

							return new EvaluatorException(message);
						}
					});

			// Close the input stream first, and then open the output stream,
			// in case the output file should override the input file.
			in.close();
			in = null;

			boolean munge = true;
			boolean warn = true;
			boolean preserveAllSemiColons = true;
			boolean preserveStringLiterals = false;

			//app.info("Compressing JS");

			compressor.compress(out, -1, munge, warn, preserveAllSemiColons,
					preserveStringLiterals);

		} catch (EvaluatorException e) {

			e.printStackTrace();
			// Return a special error code used specifically by the web
			// front-end.
			System.exit(2);

		}

	}

	/**
	 * Generate code
	 * 
	 * @param out
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public void writeCode(Writer out) throws FileNotFoundException, IOException {

		// <script>
		JavaScript.writeOpenScript(out);

		// write the application code.
		JavaScript.writeJs(buffer, out);
				
		// </script>
		JavaScript.writeCloseScript(out);
	}

	public void writeJs(Writer out) throws FileNotFoundException, IOException {
		
		// write the application code.
		JavaScript.writeJs(buffer, out);
		
	}
	

	/**
	 * Overided processLine function from Content.
	 * 
	 * @Override
	 */
	public String processLine(String input) {

		if (paramRegex.matcher(input).find()) {

			String token = input.substring(input.indexOf("@") + 1);
			String[] tokens = token.split(" ", 2);

			String param = tokens[0].trim();
			
			// Search for annotations.
			if (tokens.length == 2) {
				String value = tokens[1].trim();

				if (param.equals("title")) {
					app.setTitle(value);
				} else if (param.equals("description")) {
					app.setDescription(value);
				} else if (param.equals("id")) {
					app.setId(value);
				} else if (param.equals("thumbnail")) {
					app.setThumbnail(value);
				} else if (param.equals("height")) {
					app.setHeight(Integer.parseInt(value));
				} else if (param.equals("include")) {
					if ( value.endsWith(".css") ) {
						app.addCss(value);
					} else if ( value.endsWith(".js") ) {
						app.addJavaScript(value);
					} else {
						app.addInclude(value);
					}
				} else if (param.equals("templates")) {
					app.addTemplate(value);
				} else if (param.equals("require")) {
					app.addRequire(value);
				} else if (param.equals("css")) {
					app.addCss(value);
				} else if (param.equals("js")) {
					app.addJavaScript(value);
				} else if (param.equals("externaljs")) {
					value = replacePlaceHolders(value);
					app.addExternalJavaScript(value);
				}
				
			} else if (tokens.length == 1) {
				if (param.equals("singleton")) {
					app.setSingleton(true);
				}
			}
		}

		// Remove lines with console.debug. - I know, it's dangerous :)
		if ( !Application.getInstance().isDebug() ) {
			if ( Pattern.compile("console.debug").matcher(input).find() ) {
				return "";
			}
		}

		// Replace $INSTANCE & $HOST
		input = replacePlaceHolders(input);
				
		return input;
	}
	
	private static String replacePlaceHolders(String in)
	{
		in = in.replaceAll("\\$INSTANCE", Application.getInstance().getId());
		in = in.replaceAll("\\$HOST", Application.getInstance().getHost());
		in = in.replaceAll("\\$initMethod", "onInit");
		return in;
	}

}
